package cn.quang.search.toolkit.component.items;

import cn.quang.search.toolkit.component.ISearch;
import cn.quang.search.toolkit.component.items.impl.*;
import cn.quang.search.toolkit.enums.DataSourceTypeEnum;
import cn.quang.search.toolkit.enums.SearchOperator;

import java.util.*;
import java.util.stream.Collectors;


public class SearchItemFactory {

    static final String ARRAY_SPLIT_QUOTE = ",";

    private final DataSourceTypeEnum dsType;
    public SearchItemFactory(DataSourceTypeEnum dsType){
        this.dsType = dsType;
    }

    // 普通搜索
    public <T> ISearch getSearch(String col, SearchOperator operator, T value) {
        SearchItemGenerateEnum generateEnum = SearchItemGenerateEnum.getGenerate(operator);
        if(generateEnum == null) {
            throw new IllegalArgumentException("不支持的操作符类型：" + operator);
        }

        return generateEnum.generate(dsType, col, value);
    }

    public <T> ISearch getCustomSearch(String conditionSql){
        return new CustomSearchItem(conditionSql);
    }

    // 地图经纬度搜索
    public <T> ISearch getGeoSearch(String col, double longitude, double latitude, String distance){
        return new GeoSearchItem(dsType, col, longitude, latitude, distance);
    }

    // Nested搜索
    public <T> ISearch getNestedSearch(String col, ISearch key, List<ISearch> values){
        return new KeyValueNestedSearchItem(dsType, col, key, values);
    }

    // BetweenAnd
    public <T> ISearch getBetweenSearch(String col, T fromValue, T toValue){
        return new BetweenSearchItem(dsType, col, fromValue, toValue);
    }

    enum SearchItemGenerateEnum{

        IN(SearchOperator.In){
            @Override
            public ISearch generate(DataSourceTypeEnum dsType, String field, Object value) {
                return parseToArraySearchItem(dsType, field, SearchOperator.In, value);
            }
        },
        NOT_IN(SearchOperator.NotIn){
            @Override
            public ISearch generate(DataSourceTypeEnum dsType, String field, Object value) {
                return parseToArraySearchItem(dsType, field, SearchOperator.NotIn, value);
            }
        },
        EQUAL(SearchOperator.Equal),
        NOT_EQUAL(SearchOperator.NotEqual),
        GREAT_THAN(SearchOperator.GreaterThan),
        GREAT_THAN_OR_EQUAL(SearchOperator.GreaterThanOrEqual),
        LESS_THAN(SearchOperator.LessThan),
        LESS_THAN_OR_EQUAL(SearchOperator.LessThanOrEqual),
        IS_NULL(SearchOperator.IsNull),
        NOT_NULL(SearchOperator.NotNull){
            @Override
            public ISearch generate(DataSourceTypeEnum dsType, String field, Object value) {
                return new NotNullSearchItem(dsType, field, this.operator);
            }
        },
        LIKE(SearchOperator.Like){
            @Override
            public ISearch generate(DataSourceTypeEnum dataSourceTypeEnum, String field, Object value) {
                return new LikeSearchItem(dataSourceTypeEnum, field, this.operator, value);
            }
        },
        LEFT_LIKE(SearchOperator.LeftLike){
            @Override
            public ISearch generate(DataSourceTypeEnum dsType, String field, Object value) {
                return new LikeSearchItem(dsType, field, this.operator, value);
            }
        },
        RIGHT_LIKE(SearchOperator.RightLike){
            @Override
            public ISearch generate(DataSourceTypeEnum dsType, String field, Object value) {
                return new LikeSearchItem(dsType, field, this.operator, value);
            }
        }
        ;

        final SearchOperator operator;
        SearchItemGenerateEnum(SearchOperator operator){
            this.operator = operator;
        }

        public ISearch generate(DataSourceTypeEnum dsType, String field, Object value){
            if(this.operator == SearchOperator.IsNull){
                if(value == null) {
                    throw new IllegalStateException("您指定使用isNull查询，但指定了查询Value，此操作不合法，请检查");
                }
            }
            return new SearchItem(dsType, field, this.operator, value);
        }

        public static SearchItemGenerateEnum getGenerate(SearchOperator operator){
            return Arrays.stream(values()).filter(input -> input.operator == operator).findFirst().orElse(null);
        }
    }

    /**
     * 规则解释：
     * 1. 如果String字符串中包含逗号分隔符，则拆解成对应的集合
     * 2. 如果Object传入的是Array类型，则转换成对应类型的集合
     * 3. 如果是Collection类型，则转换成对应类型的集合
     *
     * @param dsType 表
     * @param field ES字段列表
     * @param value 待转换的值
     * @return 集合
     */
    @SuppressWarnings("unchecked")
    private static ISearch parseToArraySearchItem(DataSourceTypeEnum dsType, String field, SearchOperator operator, Object value){

        if(value == null){
            throw new IllegalStateException("集合查询不可以传递空数据");
        }

        // 对于完成度字段，兼容处理
        List<?> targetArray;
        // 如果是Array类型，则转换成对应类型的集合
        if(value.getClass().isArray()){
            Object[] targetValue = (Object[])value;
            targetArray = Arrays.stream(targetValue).collect(Collectors.toList());
        }
        // 如果是Collection类型，则转换成对应类型的集合
        else if(value instanceof Collection){
            Collection<Object> targetValue = (Collection<Object>)value;
            targetArray = new ArrayList<>(targetValue);
        }
        // 如果是String类型，则根据逗号拆解成对应类型的集合
        else if(value instanceof String){
            String targetValue = (String)value;
            String[] array = targetValue.split(ARRAY_SPLIT_QUOTE);
            targetArray = Arrays.stream(array).collect(Collectors.toList());
        }
        // 如果暂时无法确定类型，则直接用value的String类型
        else{
            targetArray = Collections.singletonList(value);
        }
        return SearchOperator.In == operator ? (targetArray.size() == 1 ?
                SearchItemGenerateEnum.getGenerate(SearchOperator.Equal).generate(dsType, field, targetArray.get(0)) : new InSearchItem(dsType, field, targetArray))
                : (SearchOperator.NotIn == operator ? new NotInSearchItem(dsType, field, targetArray)  : SearchItemGenerateEnum.getGenerate(SearchOperator.Equal).generate(dsType, field, targetArray.get(0)));
    }

}
